"""
基础页面类
提供通用的页面操作方法
"""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from utils.logger import Logger
from config.test_config import TestConfig


class BasePage:
    """基础页面类"""
    
    def __init__(self, driver):
        self.driver = driver
        self.logger = Logger().get_logger()
        self.wait = WebDriverWait(driver, TestConfig.IMPLICIT_WAIT)
        self.actions = ActionChains(driver)
    
    def find_element(self, locator, timeout=None):
        """
        查找单个元素
        
        Args:
            locator (tuple): 元素定位器 (By, value)
            timeout (int): 超时时间
            
        Returns:
            WebElement: 找到的元素
        """
        try:
            if timeout is None:
                timeout = TestConfig.IMPLICIT_WAIT
            
            wait = WebDriverWait(self.driver, timeout)
            element = wait.until(EC.presence_of_element_located(locator))
            self.logger.debug(f"找到元素: {locator}")
            return element
            
        except TimeoutException:
            self.logger.error(f"元素未找到: {locator}")
            raise
    
    def find_elements(self, locator, timeout=None):
        """
        查找多个元素
        
        Args:
            locator (tuple): 元素定位器 (By, value)
            timeout (int): 超时时间
            
        Returns:
            List[WebElement]: 找到的元素列表
        """
        try:
            if timeout is None:
                timeout = TestConfig.IMPLICIT_WAIT
            
            wait = WebDriverWait(self.driver, timeout)
            elements = wait.until(EC.presence_of_all_elements_located(locator))
            self.logger.debug(f"找到{len(elements)}个元素: {locator}")
            return elements
            
        except TimeoutException:
            self.logger.error(f"元素未找到: {locator}")
            return []
    
    def click(self, locator, timeout=None):
        """
        点击元素
        
        Args:
            locator (tuple): 元素定位器
            timeout (int): 超时时间
        """
        try:
            element = self.find_element(locator, timeout)
            element.click()
            self.logger.info(f"点击元素: {locator}")
            
        except Exception as e:
            self.logger.error(f"点击元素失败: {locator}, 错误: {str(e)}")
            raise
    
    def input_text(self, locator, text, clear=True, timeout=None):
        """
        输入文本
        
        Args:
            locator (tuple): 元素定位器
            text (str): 要输入的文本
            clear (bool): 是否清空原有内容
            timeout (int): 超时时间
        """
        try:
            element = self.find_element(locator, timeout)
            
            if clear:
                element.clear()
            
            element.send_keys(text)
            self.logger.info(f"输入文本: {locator}, 内容: {text}")
            
        except Exception as e:
            self.logger.error(f"输入文本失败: {locator}, 错误: {str(e)}")
            raise
    
    def get_text(self, locator, timeout=None):
        """
        获取元素文本
        
        Args:
            locator (tuple): 元素定位器
            timeout (int): 超时时间
            
        Returns:
            str: 元素文本
        """
        try:
            element = self.find_element(locator, timeout)
            text = element.text
            self.logger.debug(f"获取文本: {locator}, 内容: {text}")
            return text
            
        except Exception as e:
            self.logger.error(f"获取文本失败: {locator}, 错误: {str(e)}")
            return ""
    
    def get_attribute(self, locator, attribute, timeout=None):
        """
        获取元素属性
        
        Args:
            locator (tuple): 元素定位器
            attribute (str): 属性名
            timeout (int): 超时时间
            
        Returns:
            str: 属性值
        """
        try:
            element = self.find_element(locator, timeout)
            value = element.get_attribute(attribute)
            self.logger.debug(f"获取属性: {locator}, {attribute}={value}")
            return value
            
        except Exception as e:
            self.logger.error(f"获取属性失败: {locator}, {attribute}, 错误: {str(e)}")
            return ""
    
    def is_element_present(self, locator, timeout=None):
        """
        检查元素是否存在
        
        Args:
            locator (tuple): 元素定位器
            timeout (int): 超时时间
            
        Returns:
            bool: 元素是否存在
        """
        try:
            self.find_element(locator, timeout)
            return True
        except TimeoutException:
            return False
    
    def is_element_visible(self, locator, timeout=None):
        """
        检查元素是否可见
        
        Args:
            locator (tuple): 元素定位器
            timeout (int): 超时时间
            
        Returns:
            bool: 元素是否可见
        """
        try:
            if timeout is None:
                timeout = TestConfig.IMPLICIT_WAIT
            
            wait = WebDriverWait(self.driver, timeout)
            wait.until(EC.visibility_of_element_located(locator))
            return True
            
        except TimeoutException:
            return False
    
    def wait_for_element_clickable(self, locator, timeout=None):
        """
        等待元素可点击
        
        Args:
            locator (tuple): 元素定位器
            timeout (int): 超时时间
            
        Returns:
            WebElement: 可点击的元素
        """
        try:
            if timeout is None:
                timeout = TestConfig.IMPLICIT_WAIT
            
            wait = WebDriverWait(self.driver, timeout)
            element = wait.until(EC.element_to_be_clickable(locator))
            self.logger.debug(f"元素可点击: {locator}")
            return element
            
        except TimeoutException:
            self.logger.error(f"元素不可点击: {locator}")
            raise
    
    def scroll_to_element(self, locator):
        """
        滚动到元素位置
        
        Args:
            locator (tuple): 元素定位器
        """
        try:
            element = self.find_element(locator)
            self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
            time.sleep(0.5)  # 等待滚动完成
            self.logger.debug(f"滚动到元素: {locator}")
            
        except Exception as e:
            self.logger.error(f"滚动到元素失败: {locator}, 错误: {str(e)}")
            raise
    
    def hover_element(self, locator):
        """
        鼠标悬停元素
        
        Args:
            locator (tuple): 元素定位器
        """
        try:
            element = self.find_element(locator)
            self.actions.move_to_element(element).perform()
            self.logger.debug(f"鼠标悬停元素: {locator}")
            
        except Exception as e:
            self.logger.error(f"鼠标悬停失败: {locator}, 错误: {str(e)}")
            raise
    
    def switch_to_frame(self, frame_locator):
        """
        切换到iframe
        
        Args:
            frame_locator (tuple): iframe定位器
        """
        try:
            frame = self.find_element(frame_locator)
            self.driver.switch_to.frame(frame)
            self.logger.info(f"切换到iframe: {frame_locator}")
            
        except Exception as e:
            self.logger.error(f"切换iframe失败: {frame_locator}, 错误: {str(e)}")
            raise
    
    def switch_to_default_content(self):
        """切换回主文档"""
        try:
            self.driver.switch_to.default_content()
            self.logger.info("切换回主文档")
            
        except Exception as e:
            self.logger.error(f"切换回主文档失败: {str(e)}")
            raise
    
    def accept_alert(self):
        """接受警告框"""
        try:
            alert = self.driver.switch_to.alert
            alert.accept()
            self.logger.info("接受警告框")
            
        except Exception as e:
            self.logger.error(f"接受警告框失败: {str(e)}")
            raise
    
    def dismiss_alert(self):
        """取消警告框"""
        try:
            alert = self.driver.switch_to.alert
            alert.dismiss()
            self.logger.info("取消警告框")
            
        except Exception as e:
            self.logger.error(f"取消警告框失败: {str(e)}")
            raise
    
    def get_alert_text(self):
        """
        获取警告框文本
        
        Returns:
            str: 警告框文本
        """
        try:
            alert = self.driver.switch_to.alert
            text = alert.text
            self.logger.info(f"警告框文本: {text}")
            return text
            
        except Exception as e:
            self.logger.error(f"获取警告框文本失败: {str(e)}")
            return ""
    
    def refresh_page(self):
        """刷新页面"""
        try:
            self.driver.refresh()
            self.logger.info("刷新页面")
            
        except Exception as e:
            self.logger.error(f"刷新页面失败: {str(e)}")
            raise
    
    def go_back(self):
        """返回上一页"""
        try:
            self.driver.back()
            self.logger.info("返回上一页")
            
        except Exception as e:
            self.logger.error(f"返回上一页失败: {str(e)}")
            raise
    
    def go_forward(self):
        """前进到下一页"""
        try:
            self.driver.forward()
            self.logger.info("前进到下一页")
            
        except Exception as e:
            self.logger.error(f"前进到下一页失败: {str(e)}")
            raise
    
    def get_current_url(self):
        """
        获取当前页面URL
        
        Returns:
            str: 当前页面URL
        """
        try:
            url = self.driver.current_url
            self.logger.debug(f"当前页面URL: {url}")
            return url
            
        except Exception as e:
            self.logger.error(f"获取当前URL失败: {str(e)}")
            return ""
    
    def get_page_title(self):
        """
        获取页面标题
        
        Returns:
            str: 页面标题
        """
        try:
            title = self.driver.title
            self.logger.debug(f"页面标题: {title}")
            return title
            
        except Exception as e:
            self.logger.error(f"获取页面标题失败: {str(e)}")
            return "" 